<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebGL 实时编辑器</title>
    <style>
        /* 基础样式 */
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        html,
        body {
            height: 100%;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
            line-height: 1.6;
            color: #333;
            min-height: 100vh;
            padding-bottom: 10%;
            /* 为底部按钮留出空间，占总高度的10% */
        }

        /* 网格布局 */
        .container {
            display: grid;
            grid-template:
                "vertex   fragment" 40vh
                "control  output" minmax(400px, 50vh) "error    error" auto
                / 1fr 1fr;
            gap: 1rem;
            padding: 1rem;
            height: 90vh;
            /* 调整主内容区域高度为90% */
        }

        /* 公共面板样式 */
        .panel {
            background: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
            padding: 1rem;
            display: flex;
            flex-direction: column;
        }

        .panel-title {
            font-size: 1.2rem;
            color: #2c3e50;
            margin-bottom: 1rem;
            padding-bottom: 0.5rem;
            border-bottom: 2px solid #3498db;
        }

        /* 代码编辑区 */
        .code-editor {
            grid-area: vertex;
        }

        .fragment-editor {
            grid-area: fragment;
        }

        textarea.shader-input {
            flex: 1;
            resize: vertical;
            padding: 0.8rem;
            font-family: 'Fira Code', monospace;
            font-size: 0.9rem;
            border: 1px solid #ddd;
            border-radius: 4px;
            background: #f8f9fa;
        }

        /* 控制区 */
        .control-panel {
            grid-area: control;
            align-self: start;
        }

        #compileBtn {
            width: 100%;
            padding: 1rem;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 1.1rem;
            transition: all 0.2s;
        }

        #compileBtn:hover {
            background: #2980b9;
            transform: translateY(-1px);
        }

        /* 渲染输出区 */
        .render-output {
            grid-area: output;
        }

        #renderCanvas {
            flex: 1;
            width: 100%;
            height: 100%;
            border: 1px solid #ddd;
            border-radius: 4px;
            background: repeating-conic-gradient(#eee 0% 25%, white 0% 50%) 50% / 20px 20px;
        }

        /* 错误输出区 */
        .error-log {
            grid-area: error;
        }

        #errorOutput {
            flex: 1;
            padding: 1rem;
            background: #ffeef0;
            color: #d73a49;
            border: 1px solid #fdaeb7;
            border-radius: 4px;
            overflow: auto;
            white-space: pre-wrap;
            font-family: monospace;
        }

        /* 底部浮动按钮栏 */
        .floating-buttons {
            position: fixed;
            bottom: 3vh;
            left: 0;
            right: 0;
            height: 10%;
            /* 设置按钮栏高度为页面总高度的10% */
            display: flex;
            justify-content: center;
            align-items: center;
            /* 垂直居中对齐 */
            gap: 1rem;
            padding: 1rem;
            background: rgba(255, 255, 255, 0.95);
            backdrop-filter: blur(5px);
            box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
            z-index: 100;
        }

        .floating-button {
            padding: 0.8rem 1.5rem;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 1rem;
            font-weight: 500;
            transition: all 0.2s;
            height: 60%;
            /* 按钮高度占按钮栏的60% */
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .floating-button:not(.main-button) {
            background: #f1f1f1;
            color: #333;
        }

        .floating-button:not(.main-button):hover {
            background: #e0e0e0;
        }

        .floating-button.main-button {
            background: #3498db;
            color: white;
        }

        .floating-button.main-button:hover {
            background: #2980b9;
        }

        .floating-button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        /* 移动端适配 */
        @media (max-width: 768px) {
            .container {
                grid-template:
                    "vertex"
                    "fragment"
                    "control"
                    "output"
                    "error"
                    / 1fr;
                height: auto;
                min-height: 90vh;
                /* 调整为90%以适应底部按钮栏 */
                gap: 0.8rem;
                padding: 0.8rem;
            }

            .code-editor,
            .fragment-editor {
                height: 40vh;
            }

            #renderCanvas {
                height: 60vh;
                min-height: 300px;
            }

            #compileBtn {
                padding: 1.2rem;
                font-size: 1.2rem;
            }

            .panel-title {
                font-size: 1.3rem;
            }

            .floating-buttons {
                gap: 0.5rem;
                padding: 0.8rem;
            }

            .floating-button {
                padding: 0.7rem 1rem;
                font-size: 0.9rem;
            }
        }

        @media (max-width: 480px) {
            textarea.shader-input {
                font-size: 0.8rem;
            }

            #errorOutput {
                font-size: 0.9rem;
            }

            .floating-buttons {
                flex-direction: row;
                /* 在移动端也保持横向排列 */
                flex-wrap: wrap;
                gap: 0.5rem;
            }
        }
    </style>
</head>

<body>
    <div class="container">
        <!-- 顶点着色器编辑器 -->
        <section class="panel code-editor">
            <h2 class="panel-title">顶点着色器 (Vertex Shader)</h2>
            <textarea id="vertexShader" class="shader-input" spellcheck="false">#version 300 es
in vec4 position;
void main() {
    gl_Position = position;
}</textarea>
        </section>
        <!-- 片段着色器编辑器 -->
        <section class="panel fragment-editor">
            <h2 class="panel-title">片段着色器 (Fragment Shader)</h2>
            <textarea id="fragmentShader" class="shader-input" spellcheck="false">#version 300 es
precision highp float;
out vec4 fragColor;
void main() {
    fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}</textarea>
        </section>

        <!-- 控制面板
        <div class="panel control-panel">
            <button id="compileBtn">▶ 运行着色器</button>
        </div> -->

        <!-- 渲染输出 -->
        <section class="panel render-output">
            <h2 class="panel-title">渲染预览</h2>
            <canvas id="renderCanvas"></canvas>
        </section>

        <!-- 错误输出 -->
        <section class="panel error-log">
            <h2 class="panel-title">错误信息</h2>
            <pre id="errorOutput"></pre>
        </section>
    </div>

    <!-- 底部固定按钮栏 -->
    <div class="floating-buttons">
        <button id="undoBtn" class="floating-button" disabled>↩ 撤销</button>
        <button id="redoBtn" class="floating-button" disabled>↪ 重做</button>
        <button id="compileBtn" class="floating-button main-button">▶ 运行着色器</button>
    </div>

    <script>
        class WebGLPlayground {
            constructor() {
                this.canvas = document.getElementById('renderCanvas');
                this.gl = this.canvas.getContext('webgl2');
                this.program = null;

                if (!this.gl) {
                    this.showError('您的浏览器不支持WebGL 2.0');
                    return;
                }

                document.getElementById('compileBtn').addEventListener('click', () => this.compile());
                window.addEventListener('resize', () => this.resize());
                this.resize();
                this.compile();
            }

            compile() {
                const vsSource = document.getElementById('vertexShader').value;
                const fsSource = document.getElementById('fragmentShader').value;

                if (this.program) {
                    this.gl.deleteProgram(this.program);
                }

                try {
                    this.program = this.createProgram(vsSource, fsSource);
                    this.render();
                    this.showError('');
                } catch (error) {
                    this.showError(error.message);
                }
            }

            createProgram(vsSource, fsSource) {
                const vertexShader = this.compileShader(this.gl.VERTEX_SHADER, vsSource);
                const fragmentShader = this.compileShader(this.gl.FRAGMENT_SHADER, fsSource);

                const program = this.gl.createProgram();
                this.gl.attachShader(program, vertexShader);
                this.gl.attachShader(program, fragmentShader);
                this.gl.linkProgram(program);

                if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
                    throw new Error(this.gl.getProgramInfoLog(program));
                }

                return program;
            }

            compileShader(type, source) {
                const shader = this.gl.createShader(type);
                this.gl.shaderSource(shader, source);
                this.gl.compileShader(shader);

                if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
                    const error = this.gl.getShaderInfoLog(shader);
                    this.gl.deleteShader(shader);
                    throw new Error(`着色器编译错误:\n${error}`);
                }

                return shader;
            }

            render() {
                const positions = new Float32Array([
                    -1, -1, 0,
                    3, -1, 0,
                    -1, 3, 0
                ]);

                const buffer = this.gl.createBuffer();
                this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
                this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);

                const positionLoc = this.gl.getAttribLocation(this.program, 'position');
                this.gl.enableVertexAttribArray(positionLoc);
                this.gl.vertexAttribPointer(positionLoc, 3, this.gl.FLOAT, false, 0, 0);

                this.gl.useProgram(this.program);
                this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
            }

            resize() {
                const parent = this.canvas.parentElement;
                const rect = parent.getBoundingClientRect();
                this.canvas.width = rect.width * devicePixelRatio;
                this.canvas.height = rect.height * devicePixelRatio;
                this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
            }

            showError(message) {
                document.getElementById('errorOutput').textContent = message;
            }
        }

        // 初始化应用
        new WebGLPlayground();
    </script>
</body>

</html>